# -*- coding: utf-8 -*-
"""
连续号码追踪器
分析彩票历史数据中的连续号码模式，如连续多期出现相同号码的情况
支持斜连号分析（右斜和左斜序列）
"""

import pandas as pd
import numpy as np
from typing import Dict, List, Tuple, Any
from collections import defaultdict, Counter
import json

class ConsecutivePatternTracker:
    """
    连续号码模式追踪器
    用于分析历史数据中的连续出现模式和斜连号模式
    """
    
    def __init__(self, lottery_type: str):
        """
        初始化连续模式追踪器
        
        Args:
            lottery_type: 彩票类型 ('ssq' 或 'dlt')
        """
        self.lottery_type = lottery_type
        self.consecutive_history = defaultdict(list)
        self.diagonal_history = defaultdict(list)
        self.zone_weights = {}
        
        # 设置号码范围和区域划分
        if lottery_type == 'ssq':
            self.red_range = (1, 33)
            self.blue_range = (1, 16)
            self.small_zone = range(1, 12)   # 小区：1-11
            self.medium_zone = range(12, 23) # 中区：12-22
            self.large_zone = range(23, 34)  # 大区：23-33
        else:  # dlt
            self.red_range = (1, 35)
            self.blue_range = (1, 12)
            self.small_zone = range(1, 13)   # 小区：1-12
            self.medium_zone = range(13, 25) # 中区：13-24
            self.large_zone = range(25, 36)  # 大区：25-35
        
        self.pattern_weights = {}
        
    def analyze_consecutive_patterns(self, historical_data: pd.DataFrame, 
                                   max_pattern_length: int = 5) -> Dict[str, Any]:
        """
        分析连续出现的号码模式
        
        Args:
            historical_data: 历史开奖数据
            max_pattern_length: 最大分析的连续长度
            
        Returns:
            连续模式分析结果
        """
        self.consecutive_history.clear()
        
        # 获取红球列名
        red_columns = self._get_red_ball_columns(historical_data)
        
        # 分析不同长度的连续模式
        for pattern_length in range(2, max_pattern_length + 1):
            self._analyze_consecutive_length(historical_data, red_columns, pattern_length)
        
        # 计算模式权重
        self._calculate_consecutive_weights()
        
        return {
            'consecutive_patterns': dict(self.consecutive_history),
            'pattern_weights': self.pattern_weights,
            'analysis_summary': self._generate_consecutive_summary()
        }
    
    def analyze_diagonal_patterns(self, historical_data: pd.DataFrame) -> Dict[str, Any]:
        """
        分析斜连号模式（右斜递增和左斜递减）
        
        Args:
            historical_data: 历史开奖数据
            
        Returns:
            斜连号模式分析结果
        """
        self.diagonal_history.clear()
        red_columns = self._get_red_ball_columns(historical_data)
        
        # 分析至少3期的斜连模式
        for start_idx in range(len(historical_data) - 2):
            three_periods = historical_data.iloc[start_idx:start_idx + 3]
            
            # 查找右斜模式（递增序列）
            right_diagonals = self._find_right_diagonal_sequences(
                three_periods, red_columns, start_idx
            )
            for diagonal in right_diagonals:
                pattern_key = f"right_diagonal_{diagonal['base_number']}"
                self.diagonal_history[pattern_key].append(diagonal)
            
            # 查找左斜模式（递减序列）
            left_diagonals = self._find_left_diagonal_sequences(
                three_periods, red_columns, start_idx
            )
            for diagonal in left_diagonals:
                pattern_key = f"left_diagonal_{diagonal['base_number']}"
                self.diagonal_history[pattern_key].append(diagonal)
        
        return {
            'diagonal_patterns': dict(self.diagonal_history),
            'diagonal_summary': self._generate_diagonal_summary()
        }
    
    def analyze_zone_trends(self, historical_data: pd.DataFrame, 
                          recent_periods: int = 10) -> Dict[str, Any]:
        """
        分析区域热度趋势
        
        Args:
            historical_data: 历史开奖数据
            recent_periods: 最近多少期用于趋势分析
            
        Returns:
            区域趋势分析结果
        """
        red_columns = self._get_red_ball_columns(historical_data)
        recent_data = historical_data.tail(recent_periods)
        
        zone_counts = {
            'small_zone': 0,
            'medium_zone': 0,
            'large_zone': 0
        }
        
        total_numbers = 0
        
        # 统计最近各区域的号码出现次数
        for _, row in recent_data.iterrows():
            for col in red_columns:
                if pd.notna(row[col]):
                    number = int(row[col])
                    total_numbers += 1
                    
                    if number in self.small_zone:
                        zone_counts['small_zone'] += 1
                    elif number in self.medium_zone:
                        zone_counts['medium_zone'] += 1
                    elif number in self.large_zone:
                        zone_counts['large_zone'] += 1
        
        # 计算区域权重
        if total_numbers > 0:
            self.zone_weights = {
                zone: count / total_numbers 
                for zone, count in zone_counts.items()
            }
        
        # 预测下期各区域的热度
        zone_trends = self._predict_zone_trends(zone_counts, total_numbers)
        
        return {
            'zone_counts': zone_counts,
            'zone_weights': self.zone_weights,
            'zone_trends': zone_trends,
            'total_numbers': total_numbers
        }
    
    def predict_next_numbers_by_consecutive(self, recent_sequence: List[List[int]], 
                                          top_k: int = 10) -> List[Tuple[int, float]]:
        """
        基于连续模式预测下期可能出现的号码
        
        Args:
            recent_sequence: 最近几期的开奖序列
            top_k: 返回前k个最可能的号码
            
        Returns:
            号码及其概率的元组列表
        """
        number_probabilities = defaultdict(float)
        
        # 检查当前序列是否匹配任何连续模式
        for pattern_key, pattern_history in self.consecutive_history.items():
            if self._matches_consecutive_pattern(recent_sequence, pattern_key):
                # 从历史中统计这种模式后出现的号码
                for record in pattern_history:
                    next_period_numbers = record.get('next_period_numbers', [])
                    pattern_weight = self.pattern_weights.get(pattern_key, 0.1)
                    
                    for number in next_period_numbers:
                        number_probabilities[number] += pattern_weight
        
        # 归一化概率
        total_prob = sum(number_probabilities.values())
        if total_prob > 0:
            for number in number_probabilities:
                number_probabilities[number] /= total_prob
        
        # 按概率排序并返回前k个
        sorted_numbers = sorted(number_probabilities.items(), 
                              key=lambda x: x[1], reverse=True)
        
        return sorted_numbers[:top_k]
    
    def predict_next_numbers_by_diagonal(self, recent_sequence: List[List[int]], 
                                       top_k: int = 10) -> List[Tuple[int, float]]:
        """
        基于斜连模式预测下期可能出现的号码
        
        Args:
            recent_sequence: 最近几期的开奖序列
            top_k: 返回前k个最可能的号码
            
        Returns:
            号码及其概率的元组列表
        """
        number_probabilities = defaultdict(float)
        
        if len(recent_sequence) >= 2:
            last_two_periods = recent_sequence[-2:]
            
            # 检查右斜模式延续
            for numbers_in_first in last_two_periods[0]:
                for numbers_in_second in last_two_periods[1]:
                    if numbers_in_second == numbers_in_first + 1:
                        # 可能的右斜延续
                        next_number = numbers_in_second + 1
                        if self._is_valid_red_number(next_number):
                            number_probabilities[next_number] += 0.3
            
            # 检查左斜模式延续
            for numbers_in_first in last_two_periods[0]:
                for numbers_in_second in last_two_periods[1]:
                    if numbers_in_second == numbers_in_first - 1:
                        # 可能的左斜延续
                        next_number = numbers_in_second - 1
                        if self._is_valid_red_number(next_number):
                            number_probabilities[next_number] += 0.3
        
        # 从历史斜连模式中获取额外的预测
        for pattern_key, diagonal_list in self.diagonal_history.items():
            for diagonal in diagonal_list:
                if self._matches_diagonal_start(recent_sequence, diagonal):
                    next_numbers = diagonal.get('predicted_next', [])
                    for number in next_numbers:
                        number_probabilities[number] += 0.2
        
        # 归一化并排序
        total_prob = sum(number_probabilities.values())
        if total_prob > 0:
            for number in number_probabilities:
                number_probabilities[number] /= total_prob
        
        sorted_numbers = sorted(number_probabilities.items(), 
                              key=lambda x: x[1], reverse=True)
        
        return sorted_numbers[:top_k]
    
    def get_zone_weighted_numbers(self, zone_trends: Dict[str, float], 
                                num_numbers: int = 6) -> List[int]:
        """
        基于区域权重生成号码
        
        Args:
            zone_trends: 区域趋势权重
            num_numbers: 需要生成的号码数量
            
        Returns:
            基于区域权重的号码列表
        """
        selected_numbers = []
        
        # 根据权重分配各区域的号码数量
        small_count = int(num_numbers * zone_trends.get('small_zone', 0.33))
        medium_count = int(num_numbers * zone_trends.get('medium_zone', 0.33))
        large_count = num_numbers - small_count - medium_count
        
        # 从各区域随机选择号码
        if small_count > 0:
            small_numbers = np.random.choice(
                list(self.small_zone), 
                size=min(small_count, len(self.small_zone)), 
                replace=False
            ).tolist()
            selected_numbers.extend(small_numbers)
        
        if medium_count > 0:
            medium_numbers = np.random.choice(
                list(self.medium_zone), 
                size=min(medium_count, len(self.medium_zone)), 
                replace=False
            ).tolist()
            selected_numbers.extend(medium_numbers)
        
        if large_count > 0:
            large_numbers = np.random.choice(
                list(self.large_zone), 
                size=min(large_count, len(self.large_zone)), 
                replace=False
            ).tolist()
            selected_numbers.extend(large_numbers)
        
        # 如果号码不足，从整个范围补充
        while len(selected_numbers) < num_numbers:
            candidate = np.random.randint(self.red_range[0], self.red_range[1] + 1)
            if candidate not in selected_numbers:
                selected_numbers.append(candidate)
        
        return sorted(selected_numbers[:num_numbers])
    
    def _get_red_ball_columns(self, data: pd.DataFrame) -> List[str]:
        """获取红球列名"""
        red_columns = []
        for col in data.columns:
            if any(keyword in col for keyword in ['Red_', '红球_', 'red', '前区']):
                red_columns.append(col)
        return sorted(red_columns)
    
    def _analyze_consecutive_length(self, historical_data: pd.DataFrame, 
                                  red_columns: List[str], pattern_length: int):
        """分析指定长度的连续模式"""
        for number in range(self.red_range[0], self.red_range[1] + 1):
            consecutive_sequences = self._find_consecutive_sequences(
                historical_data, red_columns, number, pattern_length
            )
            
            if consecutive_sequences:
                pattern_key = f"consecutive_{number}_{pattern_length}"
                self.consecutive_history[pattern_key].extend(consecutive_sequences)
    
    def _find_consecutive_sequences(self, historical_data: pd.DataFrame, 
                                  red_columns: List[str], target_number: int, 
                                  pattern_length: int) -> List[Dict]:
        """查找指定号码的连续出现序列"""
        sequences = []
        consecutive_count = 0
        start_index = None
        
        for idx, row in historical_data.iterrows():
            # 检查当前期是否包含目标号码
            period_numbers = [row[col] for col in red_columns if pd.notna(row[col])]
            
            if target_number in period_numbers:
                if consecutive_count == 0:
                    start_index = idx
                consecutive_count += 1
            else:
                # 连续序列中断
                if consecutive_count >= pattern_length:
                    # 记录这个连续序列
                    next_period_numbers = []
                    if idx < len(historical_data):
                        next_row = historical_data.iloc[idx]
                        next_period_numbers = [next_row[col] for col in red_columns 
                                             if pd.notna(next_row[col])]
                    
                    sequences.append({
                        'number': target_number,
                        'start_period': start_index,
                        'end_period': idx - 1,
                        'length': consecutive_count,
                        'next_period_numbers': next_period_numbers
                    })
                
                consecutive_count = 0
                start_index = None
        
        # 检查最后一个序列
        if consecutive_count >= pattern_length:
            sequences.append({
                'number': target_number,
                'start_period': start_index,
                'end_period': len(historical_data) - 1,
                'length': consecutive_count,
                'next_period_numbers': []  # 没有下期数据
            })
        
        return sequences
    
    def _find_right_diagonal_sequences(self, three_periods: pd.DataFrame, 
                                     red_columns: List[str], start_idx: int) -> List[Dict]:
        """查找右斜序列（递增）"""
        sequences = []
        
        for period_idx in range(len(three_periods)):
            if period_idx >= len(three_periods) - 2:
                continue
                
            current_numbers = [three_periods.iloc[period_idx][col] 
                             for col in red_columns 
                             if pd.notna(three_periods.iloc[period_idx][col])]
            
            for base_number in current_numbers:
                # 检查是否存在递增序列
                if (self._check_number_in_period(three_periods.iloc[period_idx + 1], 
                                               red_columns, base_number + 1) and
                    self._check_number_in_period(three_periods.iloc[period_idx + 2], 
                                               red_columns, base_number + 2)):
                    
                    sequences.append({
                        'type': 'right_diagonal',
                        'base_number': base_number,
                        'sequence': [base_number, base_number + 1, base_number + 2],
                        'start_period': start_idx + period_idx,
                        'predicted_next': [base_number + 3] if self._is_valid_red_number(base_number + 3) else []
                    })
        
        return sequences
    
    def _find_left_diagonal_sequences(self, three_periods: pd.DataFrame, 
                                    red_columns: List[str], start_idx: int) -> List[Dict]:
        """查找左斜序列（递减）"""
        sequences = []
        
        for period_idx in range(len(three_periods)):
            if period_idx >= len(three_periods) - 2:
                continue
                
            current_numbers = [three_periods.iloc[period_idx][col] 
                             for col in red_columns 
                             if pd.notna(three_periods.iloc[period_idx][col])]
            
            for base_number in current_numbers:
                # 检查是否存在递减序列
                if (base_number >= 3 and
                    self._check_number_in_period(three_periods.iloc[period_idx + 1], 
                                               red_columns, base_number - 1) and
                    self._check_number_in_period(three_periods.iloc[period_idx + 2], 
                                               red_columns, base_number - 2)):
                    
                    sequences.append({
                        'type': 'left_diagonal',
                        'base_number': base_number,
                        'sequence': [base_number, base_number - 1, base_number - 2],
                        'start_period': start_idx + period_idx,
                        'predicted_next': [base_number - 3] if self._is_valid_red_number(base_number - 3) else []
                    })
        
        return sequences
    
    def _check_number_in_period(self, period_row: pd.Series, red_columns: List[str], 
                              target_number: int) -> bool:
        """检查指定号码是否在某期中出现"""
        period_numbers = [period_row[col] for col in red_columns if pd.notna(period_row[col])]
        return target_number in period_numbers
    
    def _is_valid_red_number(self, number: int) -> bool:
        """检查是否为有效的红球号码"""
        return self.red_range[0] <= number <= self.red_range[1]
    
    def _calculate_consecutive_weights(self):
        """计算连续模式的权重"""
        self.pattern_weights.clear()
        
        for pattern_key, sequences in self.consecutive_history.items():
            if sequences:
                # 权重基于出现频率和长度
                total_occurrences = len(sequences)
                avg_length = sum(seq['length'] for seq in sequences) / total_occurrences
                
                # 长度越长，频率越高，权重越大
                weight = (total_occurrences * avg_length) / 100.0
                self.pattern_weights[pattern_key] = min(weight, 1.0)  # 限制最大权重为1.0
    
    def _generate_consecutive_summary(self) -> Dict[str, Any]:
        """生成连续模式分析摘要"""
        summary = {
            'total_patterns': len(self.consecutive_history),
            'most_frequent_numbers': [],
            'longest_sequences': []
        }
        
        # 找出最频繁出现连续模式的号码
        number_frequency = defaultdict(int)
        max_length = 0
        longest_sequences = []
        
        for pattern_key, sequences in self.consecutive_history.items():
            if sequences:
                number = sequences[0]['number']
                number_frequency[number] += len(sequences)
                
                for seq in sequences:
                    if seq['length'] > max_length:
                        max_length = seq['length']
                        longest_sequences = [seq]
                    elif seq['length'] == max_length:
                        longest_sequences.append(seq)
        
        # 排序并获取前5个最频繁的号码
        most_frequent = sorted(number_frequency.items(), key=lambda x: x[1], reverse=True)[:5]
        summary['most_frequent_numbers'] = most_frequent
        summary['longest_sequences'] = longest_sequences[:3]  # 最长的3个序列
        
        return summary
    
    def _generate_diagonal_summary(self) -> Dict[str, Any]:
        """生成斜连模式分析摘要"""
        right_count = sum(1 for key in self.diagonal_history.keys() if 'right_diagonal' in key)
        left_count = sum(1 for key in self.diagonal_history.keys() if 'left_diagonal' in key)
        
        return {
            'right_diagonal_patterns': right_count,
            'left_diagonal_patterns': left_count,
            'total_diagonal_patterns': right_count + left_count
        }
    
    def _matches_consecutive_pattern(self, recent_sequence: List[List[int]], 
                                   pattern_key: str) -> bool:
        """检查最近序列是否匹配连续模式"""
        # 从模式键中提取号码和长度信息
        parts = pattern_key.split('_')
        if len(parts) >= 3 and parts[0] == 'consecutive':
            target_number = int(parts[1])
            pattern_length = int(parts[2])
            
            # 检查最近几期是否连续出现该号码
            consecutive_count = 0
            for period_numbers in reversed(recent_sequence):
                if target_number in period_numbers:
                    consecutive_count += 1
                else:
                    break
            
            return consecutive_count >= pattern_length
        
        return False
    
    def _matches_diagonal_start(self, recent_sequence: List[List[int]], 
                              diagonal: Dict) -> bool:
        """检查最近序列是否匹配斜连模式的开始"""
        if len(recent_sequence) < 2:
            return False
        
        diagonal_type = diagonal['type']
        base_number = diagonal['base_number']
        
        if diagonal_type == 'right_diagonal':
            # 检查是否有递增趋势开始
            return (base_number in recent_sequence[-2] and 
                   (base_number + 1) in recent_sequence[-1])
        elif diagonal_type == 'left_diagonal':
            # 检查是否有递减趋势开始
            return (base_number in recent_sequence[-2] and 
                   (base_number - 1) in recent_sequence[-1])
        
        return False
    
    def _predict_zone_trends(self, zone_counts: Dict[str, int], 
                           total_numbers: int) -> Dict[str, float]:
        """预测下期各区域的热度趋势"""
        zone_trends = {}
        
        if total_numbers == 0:
            # 如果没有数据，使用平均权重
            return {'small_zone': 0.33, 'medium_zone': 0.33, 'large_zone': 0.34}
        
        # 计算各区域的相对热度
        for zone, count in zone_counts.items():
            relative_frequency = count / total_numbers
            
            # 应用趋势预测逻辑：如果某区域最近很热，可能会降温
            # 如果某区域最近较冷，可能会升温
            if relative_frequency > 0.4:  # 很热
                trend_factor = 0.8  # 降温
            elif relative_frequency < 0.25:  # 较冷
                trend_factor = 1.2  # 升温
            else:
                trend_factor = 1.0  # 保持
            
            zone_trends[zone] = relative_frequency * trend_factor
        
        # 归一化权重
        total_trend = sum(zone_trends.values())
        if total_trend > 0:
            for zone in zone_trends:
                zone_trends[zone] /= total_trend
        
        return zone_trends
    
    def save_analysis_results(self, filepath: str) -> bool:
        """
        保存分析结果到文件
        
        Args:
            filepath: 保存路径
            
        Returns:
            是否保存成功
        """
        try:
            results = {
                'lottery_type': self.lottery_type,
                'consecutive_history': dict(self.consecutive_history),
                'diagonal_history': dict(self.diagonal_history),
                'pattern_weights': self.pattern_weights,
                'zone_weights': self.zone_weights
            }
            
            with open(filepath, 'w', encoding='utf-8') as f:
                json.dump(results, f, ensure_ascii=False, indent=2)
            
            return True
        except Exception as e:
            print(f"保存分析结果失败: {e}")
            return False
    
    def load_analysis_results(self, filepath: str) -> bool:
        """
        从文件加载分析结果
        
        Args:
            filepath: 文件路径
            
        Returns:
            是否加载成功
        """
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                results = json.load(f)
            
            self.lottery_type = results.get('lottery_type', self.lottery_type)
            self.consecutive_history = defaultdict(list, results.get('consecutive_history', {}))
            self.diagonal_history = defaultdict(list, results.get('diagonal_history', {}))
            self.pattern_weights = results.get('pattern_weights', {})
            self.zone_weights = results.get('zone_weights', {})
            
            return True
        except Exception as e:
            print(f"加载分析结果失败: {e}")
            return False